﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Timers;
using System.Threading;

using EITSurferInfinityLib;
using System.Reflection;
using System.Diagnostics;

namespace ControlerLib
{
    /// <summary>
    /// Interaction logic for ProtocolControler.xaml
    /// </summary>
    public partial class ProtocolControler : UserControl
    {
        VirtualDevice Device;
        string[] Protocol;
        int ProtocolPointer;
        string ActiveDir;
        System.Timers.Timer timer1;
        string var1Name;
        string var1Value;
        int InterInstruction_Time;
        double [] time_s;

        public ProtocolControler(ref VirtualDevice device)
        {
            InitializeComponent();
            Device = device;
            InterInstruction_Time = 200;
        }


        //private void button1_Click(object sender, RoutedEventArgs e)
        //{
        //    ParamManager.ProgramParameterByName("CurrentParameter.txt", "NCO_F", "5", ref Device);
        //    ParamManager.ProgramParameter("CurrentParameter.txt", ref Device);
        //    Device.StopAndStart();
        //}

        private void buttonLoadProtocol_Click(object sender, RoutedEventArgs e)
        {
            richTextBox1.Document.Blocks.Clear();
            System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                try
                {
                    string fileName = openFileDialog.FileName;
                    Protocol = Read(fileName);
                    string [] splitdir=fileName.Split('\\');
                    string dir = "";
                    for (int i = 0; i < splitdir.Length- 1; i++)
                    {
                        dir+=splitdir[i]+ "\\";
                    }
                    Protocol=ParseIncludes(Protocol,dir);
                    this.textBlock_filename.Text = fileName;
                    var1Name = null;
                    foreach (string s in Protocol)
                    {
                        richTextBox1.AppendText(s + "\r");
                    }
                    ProtocolPointer = 0;
                    EstimateTime(Protocol);
                    UpdateLabelProgress();

                }
                catch (Exception exp)
                {
                    Debug.WriteLine(exp.ToString());
                }
            }

        }

        private string[] ParseIncludes(string[] input, string dir)
        {
            List<string> temp = new List<string>();

            for (int i = 0; i < input.Length; i++)
            {
                if (input[i].Contains("include"))
                {
                    string[] s = input[i].Split(' ');
                    try
                    {
                        temp.AddRange(Read(dir+s[1]));
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("ParseIncludes: Could not find the file: " + s[1]);
                    }
                }
                else
                {
                    temp.Add(input[i]);
                }
            }

            return temp.ToArray();
        }


        void EstimateTime(string[] p)
        {
            time_s = new double[p.Length];
  
            double last_image_rate=0;
            for (int i = 0; i < p.Length; i++)
            {
                //Parse Instructions
                string[] splitInstruction = p[i].Split(' ');
                if (var1Name != null)
                {
                    for (int j = 0; j < splitInstruction.Length; j++)
                    {
                        if (splitInstruction[j].Contains(var1Name))
                        {
                            splitInstruction[j] = var1Value;
                        }
                    }
                }
                if (p[i].Contains("//"))
                {
                    //do nothing with comments
                    if (i > 0)
                    {
                        time_s[i] = time_s[i - 1] + InterInstruction_Time / 1000.0;
                    }
                }
                else if (p[i]=="")
                {
                    //do nothing with comments
                    if (i > 0)
                    {
                        time_s[i] = time_s[i - 1] + InterInstruction_Time / 1000.0;
                    }
                }
                else if ((p[i].Contains("$")) && (p[i].Contains("=")))
                {
                    //parse variables
                    string[] splitVariable = p[i].Split('=');
                    var1Name = splitVariable[0];
                    var1Value = splitVariable[1];
                    if (i > 0)
                    {
                        time_s[i] = time_s[i - 1] + InterInstruction_Time / 1000.0;
                    }
                }
                else if (p[i].Contains("set #imagerate"))
                {
                    last_image_rate=Convert.ToDouble(splitInstruction[2]);
                    if (i > 0)
                    {
                        time_s[i] = time_s[i - 1] + InterInstruction_Time / 1000.0;
                    }
                }
                else if (p[i].Contains("do frame"))
                {
                    if (i > 0)
                    {
                        time_s[i] = time_s[i - 1] + Convert.ToDouble(splitInstruction[2]) / last_image_rate;
                    }
                }
                else
                {
                    if (i > 0)
                    {
                        time_s[i] = time_s[i - 1] + InterInstruction_Time / 1000.0;
                    }
                }
            }
        }

        private void UpdateLabelProgress()
        {
            if (Protocol != null)
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    if((ProtocolPointer>1)&&(ProtocolPointer<time_s.Length))
                    {
                    labelProgress.Content = "Instructions: " + ProtocolPointer.ToString() + "/" + Protocol.Length.ToString();
                    double remaining = time_s[time_s.Length - 1] - time_s[ProtocolPointer-1];
                    remaining = Math.Round(remaining/60.0,2);
                    estimated_time.Content = "Remaining time: " + remaining.ToString() + " min";
                    }
                }));
            }
        }

        public string[] Read(string FileName)
        {
            string[] temp = null;
            try
            {
                temp = System.IO.File.ReadAllLines(FileName);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e);
            }
            return temp;
        }

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            if (Protocol != null)
            {
                ProtocolPointer = 0;
                ExecuteProtocol();
            }
        }
        
        private void ExecuteInstruction()
        {
            if ((ProtocolPointer < Protocol.Length) && (Device.Busy == false))
            {
                string instruction = Protocol[ProtocolPointer];
                if (instruction == "")
                {
                    //ProtocolPointer++;
                    //instruction=Protocol[ProtocolPointer];
                }
                else
                {
                    if (instruction.Contains("//"))
                    {
                        //do nothing with comments
                    }
                    else if ((instruction.Contains("$")) && (instruction.Contains("=")))
                    {
                        //parse variables
                        string[] splitVariable = instruction.Split('=');
                        var1Name = splitVariable[0];
                        var1Value = splitVariable[1];
                    }
                    else if (instruction == "repeat")
                    {
                        //repeat the whole protocol
                        ProtocolPointer = -1;
                    }
                    else if (instruction == "stop")
                    {
                        //stops the device
                        Device.Stop();
                    }
                    else if (instruction == "start")
                    {
                        //starts the device
                        Device.Start();
                    }
                    else if (instruction.Contains("wait"))
                    {
                        string[] splitInstruction = instruction.Split(' ');
                        string target = splitInstruction[1];
                        Thread.Sleep(Convert.ToInt32(target));
                    }
                    else if (instruction.Contains("serial"))
                    {
                        string[] splitInstruction = instruction.Split(' ');
                        string target = splitInstruction[1];
                        string msg = "";
                        for (int i = 2; i < splitInstruction.Length; i++)
                        {
                            msg += splitInstruction[i];
                        }
                        Device.SerialSend(target,msg);
                    }
                    else
                    {
                        //Parse Instructions
                        string[] splitInstruction = instruction.Split(' ');
                        if (var1Name != null)
                        {
                            for (int i = 0; i < splitInstruction.Length; i++)
                            {
                                if (splitInstruction[i].Contains(var1Name))
                                {
                                    splitInstruction[i] = var1Value;
                                }
                            }
                        }
                        switch (splitInstruction[0])
                        {
                            case "set":
                                SetParameter(splitInstruction[1], splitInstruction[2]);
                                break;
                            case "do":
                                DoTask(splitInstruction[1], splitInstruction[2]);
                                break;
                            case "dir":
                                ChangeActiveDirectory(splitInstruction[1]);
                                break;
                            case "file":
                                NewFile(splitInstruction[1]);
                                break;
                            case "save":
                                Device.Saving = true;
                                Save();
                                break;
                        }
                    }
                }

                //show the parsed instruction in the GUI 
                this.Dispatcher.Invoke((Action)(() =>
                {
                    if ((instruction!="") && !instruction.Contains("//"))
                    {
                        richTextBox2.AppendText("SENT: " + instruction + "\r");
                        richTextBox2.ScrollToEnd();
                    }
                }));

                ProtocolPointer++;

                //display number of executed instruction over total
                UpdateLabelProgress();
            }
        }
        private void ExecuteProtocol()
        {
            if ((Device.Busy == false) && (Device.BusyFrame == false) && (Device.Saving == false) && (Device.BusySerial == false))
            {
                ExecuteInstruction();
                if (ProtocolPointer < Protocol.Length)
                {
                    Thread.Sleep(200);
                    SetWaitTimer();
                }
            }
            else
            {
                SetWaitTimer();
            }
        }
        public void SetWaitTimer()
        {
            timer1=new System.Timers.Timer();
            timer1.Elapsed += new ElapsedEventHandler(timer1_tick);
            timer1.Interval = InterInstruction_Time;
            timer1.Start();
        }

        void timer1_tick(object sender, ElapsedEventArgs e)
        {
            if ((Device.Busy == true) || (Device.BusyFrame == true) || (Device.Saving == true) || (Device.BusySerial == true))
            {
                //continue waiting
                Thread.Sleep(200);
            }
            else
            {
                timer1.Stop();
                ExecuteProtocol();
            }
        }
        private void SetParameter(string param, string value)
        {
            switch (Device.DeviceName)
            {
                case "st_1_0.12_3.0_A":
                    Param_st_1_0p12.ProgramParameterByName("CurrentParameter.txt", param, value, ref Device);
                    Param_st_1_0p12.ProgramParameter("CurrentParameter.txt", ref Device);
                    break;
                case "st_1_0.12_3.0_B":
                    Param_st_1_0p12.ProgramParameterByName("CurrentParameter.txt", param, value, ref Device);
                    Param_st_1_0p12.ProgramParameter("CurrentParameter.txt", ref Device);
                    break;
                case "st_1_0.111_2.42_B":
                    Param_st_1_0p111.ProgramParameterByName("CurrentParameter.txt", param, value, ref Device);
                    Param_st_1_0p111.ProgramParameter("CurrentParameter.txt", ref Device);
                    break;
            }
            //ParamManager.ProgramParameterByName("CurrentParameter.txt", param, value, ref Device);
            //ParamManager.ProgramParameter("CurrentParameter.txt", ref Device);
            //Device.StopAndStart();
        }

        private void DoTask(string task, string value)
        {
            switch (task)
            {
                case "raw":
                    Device.Stop();
                    Thread.Sleep(200);
                    Device.Busy = true;
                    Device.SerialRaw(Convert.ToInt32(value));
                    break;
                case "frame":
                    Device.Stop();
                    Thread.Sleep(200);
                    
                    Device.BusyFrame=true;
                    Device.EnableRecordFrames = true;
                    Thread.Sleep(200);
                    Device.SerialFrame(Convert.ToInt32(value));
                    break;
            }
        }

        private void ClearTempDir()
        {
            try
            {
                string TempDir = "temp";
                string[] TempSubDir = Directory.GetDirectories(TempDir);
                for (int i = 0; i < TempSubDir.Length; i++)
                {
                    Directory.Delete(TempSubDir[i],true);
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.ToString());
            }
        }

        private void ChangeActiveDirectory(string DirName)
        {
            try
            {
                ActiveDir= "ProtocolResults\\"+DirName;
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void NewFile(string FileName)
        {
            try
            {
                Thread.Sleep(1000);
                ClearTempDir();
                Device.NewOeitFile(FileName);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void Save()
        {
            try
            {
                Device.SaveAsych(ActiveDir);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void buttonNext_Click(object sender, RoutedEventArgs e)
        {
            ExecuteProtocol();
        }

        private void button_savetofile_Click(object sender, RoutedEventArgs e)
        {
            string filename = this.textBlock_filename.Text;
            TextRange textRange = new TextRange(richTextBox1.Document.ContentStart,
                richTextBox1.Document.ContentEnd);
            List<string>lines=new List<string>();
            string[] splited = textRange.Text.Split('\r');
            for(int i=0; i<splited.Length; i++)
            {
                string temp = splited[i].Replace("\n","");
                lines.Add(temp);
            }
            System.IO.File.WriteAllLines(filename, lines.ToArray());
        }

        private void button_reload_Click(object sender, RoutedEventArgs e)
        {
            richTextBox1.Document.Blocks.Clear();
            string fileName = this.textBlock_filename.Text;
            try
            {
                Protocol = Read(fileName);
                string[] splitdir = fileName.Split('\\');
                string dir = "";
                for (int i = 0; i < splitdir.Length - 1; i++)
                {
                    dir += splitdir[i] + "\\";
                }
                Protocol = ParseIncludes(Protocol,dir);
                this.textBlock_filename.Text = fileName;
                var1Name = null;
                foreach (string s in Protocol)
                {
                    richTextBox1.AppendText(s + "\r");
                }
                ProtocolPointer = 0;
                EstimateTime(Protocol);
                UpdateLabelProgress();

            }
            catch (Exception exp)
            {
                Debug.WriteLine(exp.ToString());
            }
        }

        private void button_Stop_Click(object sender, RoutedEventArgs e)
        {
            timer1.Stop();
            ProtocolPointer = 0;
            Device.Stop();
            Device.Busy = false; 
            Device.BusyFrame = false;
            Device.Saving = false;
            Device.BusySerial = false;
        }
    }
}
